package screen;

import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.Random;

import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;

import ctrl.Control;

public class ScreenView {
	/**
	 * 
	 */
	// private static final long serialVersionUID = 1L;
	Control gui;
	int mode = 0;	/* 0 for idle
					 * 1 for receiving
					 * 2 for viewing (not used)
					 * 3 for broadcasting */
	
	boolean EndOfTransmissionPhase = false;
	boolean displaySizeInit = false;
	boolean counterOn = false;
	long receiveTime = 0L;
	Viewer vw = new Viewer();

	public ScreenView(Control g) {
		gui = g;
	}

	/*
	 * Action taken from clicking the Broadcast Screen Button if mode = 0, start
	 * broadcasting if mode = 1, you are in receiving mode if mode = 3, stop
	 * broadcasting, go to idle mode
	 */
	public void action() {
		switch (mode) {
		case 0: // Idle -> broadcast
			mode = 3;
			gui.println("System::Starting broadcast.");
			gui.screenBtnSetTxt("Stop Broadcasting");
			(new Thread(new sendScreen())).start();
			break;
		case 1: // receiving // mode=1
			if (gui.screenBtnGetTxt() == "Show Screen") {
				vw.update();
				vw.open();
				gui.screenBtnSetTxt("Close Screen");
			} else if (gui.screenBtnGetTxt() == "Close Screen") {
				vw.close();
				gui.screenBtnSetTxt("Show Screen");
			} else {
				vw.close();
				System.out.println("System Error::Not supposed to happen.");
				System.out.println("Case 1: else");
			}
			break;
		case 3: // broadcasting -> idle
			EndOfTransmissionPhase = true;
			mode = 0;
			gui.println("System::Stopping screen broadcast.");
			gui.screenBtnSetTxt("Broadcast Screen");
			try {
				Thread.sleep(1000);
			} catch (Exception e) {
				System.out.println("@ScreenView - Exception: " + e.getMessage());
			}
			EndOfTransmissionPhase = false;
			break;
		}
	}

	/*
	 * For cases when the user was transmitting and does not click on Stop
	 * Broadcasting before leaving. AND for cases when the users were receiving,
	 * and the sender left the network, causing the receivers to not receive the
	 * End of Transmission packet
	 */
	public void resetMode() {
		if (mode != 0)
			vw.close();
		mode = 0; 			// return to idle
		counterOn = false;	// closes counter thread
		return;
	}

	/*
	 * Counter thread runs to tracks when the last screen image was received
	 * Resets mode and button, and closes window if sender unexpectedly left
	 * the network
	 */
	public class counter implements Runnable {

		public void run() {
			while (counterOn) {
				// takes time shot
				long currentTime = System.currentTimeMillis();

				/*
				 * Takes a time shot, if the time is > by lastReceivedImage +
				 * 10sec Then close screen, reset mode
				 */
				if ((currentTime - receiveTime) > 10000) {
					// Reset button, and mode, close viewer
					resetMode();
					gui.screenBtnSetTxt("Broadcast Screen");
				} else {
					try {
						Thread.sleep(4000);
					} catch (Exception e) {
						System.out.println("@ScreenView - Exception: " + e.getMessage());
					}
				}
			}
			return;
		}

	}

	public class sendScreen implements Runnable {

		public void run() {
			while (mode == 3) {
				try {
					// Getting the size of screen
					Dimension screenDim = Toolkit.getDefaultToolkit()
							.getScreenSize();
					Robot robot = new Robot();
					// Taking image capture of screen from corner to corner of
					// screen
					BufferedImage image = robot
							.createScreenCapture(new Rectangle(0, 0,
									(int) screenDim.getWidth(), (int) screenDim
											.getHeight()));

					if ((int) screenDim.getWidth() > 1600) {
						image = resizeImage(image, 1600);
					}

					ByteArrayOutputStream baos = new ByteArrayOutputStream();
					ImageIO.write(image, "jpg", baos);
					baos.flush();
					byte[] data = baos.toByteArray();
					gui.getNet().sendBytes(data, 2); 	// service=2 indicates message is for screen viewer

					Thread.sleep(2000); // default is 200, if set lower, we may
										// have to change the sleep time
					// in the receive function to let all transmission arrive at
					// the other end
				} catch (Exception e) {
					System.out.println("@ScreenView - Exception screen sharing: " + e.getMessage());
				}
			}
			
			/* --------- Signaling end of transmission ----------- */
			byte[] data = new byte[1];
			new Random().nextBytes(data);
			gui.getNet().sendBytes(data, 2);
			try {
				Thread.sleep(1000);
			} catch (Exception e) {
				System.out.println("@ScreenView - Exception: " + e.getMessage());
			}
			gui.getNet().sendBytes(data, 2);
			/* --------------------- end of ---------------------- */
			
			mode = 0; // required to for proper operation of button (sender also
						// receives his own messages)
			gui.screenBtnSetTxt("Broadcast Screen");
		}
	}

	public void receive(byte[] data) {
		System.out.println("System::Screen Sharing - received ..." + data.length);
		if (mode == 3)
			return;
		if (data.length < 2) { // transmission ended
			EndOfTransmissionPhase = true;
			vw.close();
			gui.screenBtnSetTxt("Broadcast Screen");
			mode = 0;
			try {
				Thread.sleep(4000);
			} catch (Exception e) {
				System.out.println("@ScreenView - Exception: " + e.getMessage());
			}
			EndOfTransmissionPhase = false;
			displaySizeInit = false;
		} else {
			// Initialize receive time
			receiveTime = System.currentTimeMillis();

			if (!counterOn) {
				counterOn = true;
				(new Thread(new counter())).start();
			}

			if (mode == 0 && !EndOfTransmissionPhase) {
				mode = 1;
				gui.screenBtnSetTxt("Show Screen");
			}

			try {
				InputStream in = new ByteArrayInputStream(data);
				vw.img = ImageIO.read(in);
			} catch (IOException e) {
				gui.println("System::Error in received image");
				System.out.println("@ScreenView - IOException: " + e.getMessage());
			}

			if (!displaySizeInit)
					vw.SetDisplaySize();
			vw.update();
		}
	}

	// Image Adapting to window (Viewer) size
	private static BufferedImage resizeImage(BufferedImage originalImage,
			int viewerWidth) {

		double imageWidth = (double) originalImage.getWidth();
		double imageHeight = (double) originalImage.getHeight();
		double imageRatio = imageWidth / imageHeight;

		double newImageHeight = ((double) (viewerWidth)) / imageRatio;

		BufferedImage resizedImage = new BufferedImage(viewerWidth,
				(int) newImageHeight, BufferedImage.TYPE_INT_ARGB);
		Graphics2D g = resizedImage.createGraphics();
		g.drawImage(originalImage, 0, 0, viewerWidth, (int) newImageHeight,
				null);
		g.dispose();

		return resizedImage;
	}

	class Viewer {
		JFrame vwframe = new JFrame("Display Viewer");
		public Image img;
		JPanel outputPanel;

		Viewer() {
			vwframe.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
			vwframe.addWindowListener(new WindowAdapter() {
				public void windowClosing(WindowEvent e) {
					gui.screenBtnSetTxt("Show Screen");
				}
			});

			BorderLayout l = new BorderLayout();
			vwframe.getContentPane().setLayout(l);
			outputPanel = new JPanel();
			vwframe.getContentPane().add(outputPanel, BorderLayout.LINE_START);
		}

		public void open() {
			vwframe.setVisible(true);
		}

		public boolean isOpen() {
			return vwframe.isVisible();
		}

		public void close() {
			vwframe.setVisible(false);
		}

		/* This is when show screen is first clicked on, initialize display size */
		public void SetDisplaySize() {
			Dimension screenDimension = Toolkit.getDefaultToolkit()
					.getScreenSize();
			int screenWidth = (int) screenDimension.getWidth();
			int screenHeight = (int) screenDimension.getHeight();

			int imageWidth = (int) (img.getWidth(null));
			int imageHeight = (int) (img.getHeight(null));

			if (screenWidth >= imageWidth)
				vwframe.setSize(imageWidth, imageHeight);
			else
				vwframe.setSize(screenWidth, screenHeight);

			displaySizeInit = true;
		}

		public void update() {
			receiveTime = System.currentTimeMillis();

			Dimension d = vwframe.getSize();

			BufferedImage myImage = (BufferedImage) img;

			/*
			 * Resize image according to viewer size Image will resize if image
			 * width is bigger than viewer width, left untouched otherwise
			 */
			if (d.width != 0 && d.height != 0) {
				if (img.getWidth(null) > d.width)
					myImage = resizeImage((BufferedImage) img, d.width);
			}
			
			vwframe.setSize(myImage.getWidth(), myImage.getHeight());

			JLabel picLabel = new JLabel(new ImageIcon(myImage));
			picLabel.setSize(d);
			outputPanel.removeAll();
			outputPanel.add(picLabel);
			vwframe.setContentPane(outputPanel);
			outputPanel.repaint();
		}
	}
}
